package com.xyq.friend.service.question.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.github.pagehelper.util.StringUtil;
import com.xyq.api.domain.QuestionExeResult;
import com.xyq.api.domain.RemoteJudgeService;
import com.xyq.api.domain.dto.JudgeSubmitDTO;
import com.xyq.api.domain.vo.UserQuestionResultVO;
import com.xyq.common.core.constants.CacheConstants;
import com.xyq.common.core.constants.Constants;
import com.xyq.common.core.constants.HttpConstants;
import com.xyq.common.core.domain.Result;
import com.xyq.common.core.enums.ProgramType;
import com.xyq.common.core.enums.QuestionResult;
import com.xyq.common.core.enums.ResultCode;
import com.xyq.common.core.exception.ServiceException;
import com.xyq.common.core.utils.ThreadLocalUtil;
import com.xyq.common.redis.service.RedisService;
import com.xyq.friend.domain.question.Question;
import com.xyq.friend.domain.question.QuestionCase;
import com.xyq.friend.domain.question.QuestionResultVO;
import com.xyq.friend.domain.question.es.QuestionES;
import com.xyq.friend.domain.user.UserSubmit;
import com.xyq.friend.domain.user.dto.UserSubmitDTO;
import com.xyq.friend.elasticsearch.QuestionRepository;
import com.xyq.friend.mapper.QuestionMapper;
import com.xyq.friend.mapper.UserSubmitMapper;
import com.xyq.friend.rabbit.JudgeProducer;
import com.xyq.friend.service.question.UserQuestionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestHeader;

import java.util.List;

@Service
public class UserQuestionImpl implements UserQuestionService {
    @Autowired
    private QuestionRepository questionRepository ;

    @Autowired
    private QuestionMapper questionMapper ;

    @Autowired
    private RemoteJudgeService remoteJudgeService ;

    @Autowired
    private UserSubmitMapper userSubmitMapper ;

    @Autowired
    private JudgeProducer judgeProducer ; // 判题生产者

    @Autowired
    private RedisService redisService ;

    @Override
    public Result<UserQuestionResultVO> submit(UserSubmitDTO userSubmitDTO ,
                                               @RequestHeader(HttpConstants.AUTHENTICATION) String token) {
        Integer programType = userSubmitDTO.getProgramType();
        // 判断代码是什么类型的语言
        if(ProgramType.JAVA.getValue().equals(programType)){
            // 按照java的逻辑来处理
            // 处理代码的逻辑
            // 获取题目的各各个数据
            JudgeSubmitDTO judgeSubmitDTO = assembleJudeSubmitDTO(userSubmitDTO);
            // 调用judge 服务的方法判断 题目
            return remoteJudgeService.doJudgeJavaCode(judgeSubmitDTO,  token);

        }else{
            // 目前只支持 java, 所以凡是else直接抛异常
            throw new ServiceException(ResultCode.FAILED_NOT_SUPPORT_PROGRAM);
        }
    }

    // 使用 RabbitMQ 生产者-消费者模型的优化
    @Override
    public boolean rabbitSubmit(UserSubmitDTO userSubmitDTO) {
        Integer programType = userSubmitDTO.getProgramType();
        // 判断代码是什么类型的语言
        if(ProgramType.JAVA.getValue().equals(programType)){
            // 按照java的逻辑来处理
            // 处理代码的逻辑
            // 获取题目的各各个数据
            JudgeSubmitDTO judgeSubmitDTO = assembleJudeSubmitDTO(userSubmitDTO);
            //  使用 judge生产者 发送消息
             judgeProducer.produceMsg(judgeSubmitDTO);
          return true ;
        }else{
            // 目前只支持 java, 所以凡是else直接抛异常
            throw new ServiceException(ResultCode.FAILED_NOT_SUPPORT_PROGRAM);
        }
    }

    // 获取判题结果
    @Override
    public QuestionResultVO judgeResult(Long questionId, Long examId, String currentTime) {
          Long userId = ThreadLocalUtil.get(Constants.USER_ID , Long.class);
          if(userId == null){
              throw new ServiceException(ResultCode.ERROR);
          }
        UserSubmit userSubmit  = userSubmitMapper.selectResult(userId, questionId, examId , currentTime);
        QuestionResultVO questionResultVO = new QuestionResultVO() ;
        if(userSubmit == null){
            questionResultVO.setPass(QuestionResult.RUNNING.getValue());
        }else {
         questionResultVO.setExeMessage(userSubmit.getExeMessage());
         questionResultVO.setSubmitId(userSubmit.getSubmitId());
          questionResultVO.setPass(userSubmit.getPass());
          // 判断运行结果是否为空
            if(StringUtil.isNotEmpty(userSubmit.getCaseJudgeRes())){
                questionResultVO.setQuestionExeResultList(JSON.parseArray(userSubmit.getCaseJudgeRes(), QuestionExeResult.class));
            }
        }
        return questionResultVO;
    }

    // 处理请求, 将请求转换成 判题服务 需要的 dto
    private JudgeSubmitDTO assembleJudeSubmitDTO(UserSubmitDTO userSubmitDTO) {
     JudgeSubmitDTO judgeSubmitDTO = new JudgeSubmitDTO() ;
     // 从ES里获取存储的题目数据

        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        // 保存一份 代码

        if(userId != null && userSubmitDTO.getQuestionId()!=null&&userSubmitDTO.getExamId()!=null){
          String submitCodeKey =   getSubmitCodeKey(userId , userSubmitDTO.getQuestionId(),userSubmitDTO.getExamId() );
          redisService.setCacheObject(submitCodeKey,userSubmitDTO.getUserCode());
        }
                                                            // 按找题目id查找es, 如果没有返回null
        QuestionES questionES = questionRepository.findById(userSubmitDTO.getQuestionId()).orElse(null);
        if(questionES == null){
            // 如果es里没有,去数据库里查
            Question question = questionMapper.selectById(userSubmitDTO.getQuestionId());

            BeanUtil.copyProperties(question , judgeSubmitDTO);

           // 刷新ES里存储的数据
            questionES = new QuestionES();
            BeanUtil.copyProperties(question , questionES);


            questionRepository.save(questionES);
        }else{
            // 如果有将数据复制给 judeSubmitDTO
            BeanUtil.copyProperties(questionES , judgeSubmitDTO);
        }
        // 给 judgeSubmitDTO 添加其他属性的数据
        // 用户id
        judgeSubmitDTO.setUserId(ThreadLocalUtil.get(Constants.USER_ID , Long.class));
        // 竞赛id
        judgeSubmitDTO.setExamId(userSubmitDTO.getExamId());
        // 代码类型
        judgeSubmitDTO.setProgramType(userSubmitDTO.getProgramType());
        // 用户输入的代码: 用户输入的代码 + main方法 拼接
        judgeSubmitDTO.setUserCode(codeContent(userSubmitDTO.getUserCode() , questionES.getMainFuc()));
        // 测试用例: 输入用例和 输出用例
        // 用例都是JSON格式的数据,提供 实体类,将JSON用例转换成list
        List<QuestionCase> questionCaseList = JSONUtil.toList(questionES.getQuestionCase(), QuestionCase.class);
        // 将questionCase 转换为 测试用例的 输入用例list 和输出用例(预期输出) list
        List<String> inputList = questionCaseList.stream().map(QuestionCase::getInput).toList();
        List<String> outputList = questionCaseList.stream().map(QuestionCase::getOutput).toList();
        // 输入输出list给judgeDTO
        judgeSubmitDTO.setInputList(inputList);
        judgeSubmitDTO.setOutputList(outputList);
        return judgeSubmitDTO;
    }
  // 获取用户提交代码key
    private String getSubmitCodeKey(Long userId, Long questionId, Long examId) {
        return CacheConstants.SUBMIT_CODE + userId +":"+ examId+ ":" +questionId ;
    }

    // 将用户输入的代码 和 main方法 拼接到一起
    private String codeContent(String userCode, String mainFuc) {
       String targetCharacter = "}" ;
       int targetIndex = userCode.lastIndexOf(targetCharacter) ;
       if(targetIndex !=   -1){
         // 拼接代码
           return userCode.substring(0 , targetIndex)+ "\n" + mainFuc + "\n"+
                   userCode.substring(targetIndex);
       }
          // 如果拼接失败 报错
        throw new ServiceException(ResultCode.FAILED);
    }
}
